"""
API路由模块
功能：定义语音情感识别相关的API端点
参数：音频文件上传、模型训练请求等
返回值：JSON格式的API响应
使用场景：前端与后端交互的接口层
"""

from fastapi import APIRouter, UploadFile, File, HTTPException, Form, BackgroundTasks
from fastapi.responses import JSONResponse
from typing import Dict, Any, Optional
import tempfile
import os
import shutil
from pathlib import Path
import logging

from features.audio_features import AudioFeatureExtractor
from models.emotion_classifier import EmotionClassifier
from services.emotion_service import EmotionService
from services.model_service import ModelService
from core.config import settings

logger = logging.getLogger(__name__)

# 创建路由器
emotion_router = APIRouter()

# 初始化服务
emotion_service = EmotionService()
model_service = ModelService()

@emotion_router.post("/predict")
async def predict_emotion(audio_file: UploadFile = File(...)) -> Dict[str, Any]:
    """
    预测语音文件的情感类别
    参数：audio_file - 上传的音频文件
    返回值：情感预测结果和置信度
    """
    # 验证文件格式
    if not _is_valid_audio_file(audio_file.filename):
        raise HTTPException(
            status_code=400,
            detail=f"不支持的文件格式。支持的格式: {settings.ALLOWED_EXTENSIONS}"
        )
    
    # 验证文件大小
    if audio_file.size > settings.MAX_FILE_SIZE:
        raise HTTPException(
            status_code=413,
            detail=f"文件大小超出限制（最大{settings.MAX_FILE_SIZE // (1024*1024)}MB）"
        )
    
    try:
        # 保存临时文件
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_file:
            shutil.copyfileobj(audio_file.file, temp_file)
            temp_path = temp_file.name
        
        # 预测情感
        result = await emotion_service.predict_emotion(temp_path)
        
        # 清理临时文件
        os.unlink(temp_path)
        
        return {
            "success": True,
            "filename": audio_file.filename,
            "predicted_emotion": result["predicted_emotion"],
            "confidence": result["confidence"],
            "probabilities": result["probabilities"],
            "processing_time": result["processing_time"]
        }
        
    except Exception as e:
        # 清理临时文件
        if 'temp_path' in locals():
            try:
                os.unlink(temp_path)
            except:
                pass
        
        raise HTTPException(status_code=500, detail=f"预测过程中出错: {str(e)}")

@emotion_router.get("/emotions")
async def get_supported_emotions() -> Dict[str, Any]:
    """
    获取支持的情感类别列表
    返回值：支持的情感标签和描述
    """
    emotion_descriptions = {
        "neutral": "中性",
        "happy": "快乐",
        "angry": "愤怒", 
        "sad": "悲伤"
    }
    
    return {
        "success": True,
        "emotions": settings.EMOTION_LABELS,
        "descriptions": emotion_descriptions,
        "total_count": len(settings.EMOTION_LABELS)
    }

@emotion_router.get("/model/status")
async def get_model_status() -> Dict[str, Any]:
    """
    获取模型状态信息
    返回值：模型是否已加载、模型类型等信息
    """
    status = emotion_service.get_service_status()
    return {
        "success": True,
        "model_loaded": status["is_loaded"],
        "model_type": status.get("model_type", "unknown"),
        "supported_features": [
            "MFCC", "ZCR", "Chroma", "Energy", "Pitch"
        ]
    }

@emotion_router.post("/analyze")
async def analyze_audio_features(audio_file: UploadFile = File(...)) -> Dict[str, Any]:
    """
    分析音频文件的特征信息（不进行情感预测）
    参数：audio_file - 上传的音频文件
    返回值：详细的音频特征分析结果
    """
    if not _is_valid_audio_file(audio_file.filename):
        raise HTTPException(
            status_code=400,
            detail=f"不支持的文件格式。支持的格式: {settings.ALLOWED_EXTENSIONS}"
        )
    
    try:
        # 保存临时文件
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_file:
            shutil.copyfileobj(audio_file.file, temp_file)
            temp_path = temp_file.name
        
        # 分析音频特征
        analysis_result = emotion_service.analyze_audio_features(temp_path)
        
        # 清理临时文件
        os.unlink(temp_path)
        
        return {
            "success": True,
            "filename": audio_file.filename,
            "audio_info": analysis_result["audio_info"],
            "features": analysis_result["features"],
            "feature_statistics": analysis_result["statistics"]
        }
        
    except Exception as e:
        # 清理临时文件
        if 'temp_path' in locals():
            try:
                os.unlink(temp_path)
            except:
                pass
        
        raise HTTPException(status_code=500, detail=f"特征分析过程中出错: {str(e)}")

@emotion_router.post("/train")
async def initiate_training(model_type: str = Form("random_forest")) -> Dict[str, Any]:
    """
    启动模型训练（示例接口，实际应用中需要训练数据）
    参数：model_type - 模型类型
    返回值：训练状态信息
    """
    supported_models = ["svm", "knn", "random_forest"]
    
    if model_type not in supported_models:
        raise HTTPException(
            status_code=400,
            detail=f"不支持的模型类型。支持的模型: {supported_models}"
        )
    
    try:
        # 这里应该有实际的训练逻辑
        # 由于没有训练数据，返回模拟结果
        return {
            "success": True,
            "message": "训练请求已接收",
            "model_type": model_type,
            "status": "training_initiated",
            "note": "实际训练需要提供RAVDESS等标注数据集"
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"训练启动失败: {str(e)}")

def _is_valid_audio_file(filename: str) -> bool:
    """
    验证音频文件格式
    参数：filename - 文件名
    返回值：是否为有效的音频文件
    """
    if not filename:
        return False
    
    file_extension = os.path.splitext(filename.lower())[1]
    return file_extension in settings.ALLOWED_EXTENSIONS

@emotion_router.get("/health")
async def health_check() -> Dict[str, str]:
    """
    健康检查端点
    返回：服务状态信息
    """
    try:
        # 检查模型是否可用
        has_model = await model_service.get_available_models()
        model_status = "available" if has_model["has_model"] else "no_model"
        
        return {
            "status": "healthy",
            "model_status": model_status,
            "message": "语音情感识别服务运行正常"
        }
    except Exception as e:
        logger.error(f"健康检查失败: {e}")
        return {
            "status": "unhealthy",
            "model_status": "error",
            "message": f"服务异常: {str(e)}"
        }

@emotion_router.get("/models")
async def get_models() -> Dict[str, Any]:
    """
    获取所有可用模型信息
    返回：本地模型列表
    """
    try:
        models_info = await model_service.get_available_models()
        return models_info
    except Exception as e:
        logger.error(f"获取模型列表失败: {e}")
        raise HTTPException(status_code=500, detail=f"获取模型列表失败: {str(e)}")

@emotion_router.post("/models/switch")
async def switch_model(model_data: Dict[str, str]) -> Dict[str, Any]:
    """
    切换当前使用的模型
    参数：model_data - 包含模型路径或ID的数据
    返回：切换结果
    """
    try:
        model_path = model_data.get("model_path")
        model_id = model_data.get("model_id")
        
        if not model_path and not model_id:
            raise HTTPException(status_code=400, detail="必须提供model_path或model_id")
        
        # 如果提供的是model_id，转换为路径
        if model_id:
            model_path = f"models/{model_id}.pkl"
        
        result = await model_service.switch_model(model_path)
        
        # 重新加载情感服务的模型
        await emotion_service.reload_model()
        
        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"切换模型失败: {e}")
        raise HTTPException(status_code=500, detail=f"切换模型失败: {str(e)}")

@emotion_router.delete("/models/{model_id}")
async def delete_model(model_id: str) -> Dict[str, Any]:
    """
    删除指定模型
    参数：model_id - 要删除的模型ID
    返回：删除结果
    """
    try:
        result = await model_service.delete_model(model_id)
        return result
    except Exception as e:
        logger.error(f"删除模型失败: {e}")
        raise HTTPException(status_code=500, detail=f"删除模型失败: {str(e)}")

@emotion_router.get("/training/status")
async def get_training_status() -> Dict[str, Any]:
    """
    获取模型训练状态
    返回：当前训练任务的状态信息
    """
    try:
        status = await model_service.get_training_status()
        return status
    except Exception as e:
        logger.error(f"获取训练状态失败: {e}")
        raise HTTPException(status_code=500, detail=f"获取训练状态失败: {str(e)}")

@emotion_router.post("/training/start")
async def start_training(
    background_tasks: BackgroundTasks,
    request_data: Dict[str, Any]
) -> Dict[str, Any]:
    """
    开始模型训练
    参数：request_data - 包含训练参数的JSON数据
    返回：训练启动结果
    """
    try:
        # 获取训练参数
        training_params = request_data.get("training_params", {})
        model_type = training_params.get("model_type", "random_forest")
        
        # 检查是否已有训练任务在进行
        current_status = await model_service.get_training_status()
        if current_status.get("is_training", False):
            return {
                "status": "error",
                "message": "已有训练任务在进行中，请等待完成"
            }
        
        # 在后台启动训练任务
        background_tasks.add_task(
            model_service.start_training,
            training_params
        )
        
        return {
            "status": "success",
            "message": f"开始训练{model_type}模型",
            "model_type": model_type
        }
        
    except Exception as e:
        logger.error(f"启动训练失败: {e}")
        raise HTTPException(status_code=500, detail=f"启动训练失败: {str(e)}")

@emotion_router.post("/models/upload")
async def upload_model(model_file: UploadFile = File(...)) -> Dict[str, Any]:
    """
    上传自定义模型文件
    参数：model_file - 模型文件
    返回：上传结果
    """
    try:
        # 验证文件类型
        if not model_file.filename.endswith('.pkl'):
            raise HTTPException(status_code=400, detail="只支持.pkl格式的模型文件")
        
        # 保存模型文件
        models_dir = Path("models")
        models_dir.mkdir(exist_ok=True)
        model_path = models_dir / model_file.filename
        
        with open(model_path, "wb") as f:
            content = await model_file.read()
            f.write(content)
        
        logger.info(f"模型上传成功: {model_file.filename}")
        
        return {
            "status": "success",
            "message": f"模型 {model_file.filename} 上传成功",
            "model_path": str(model_path)
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"上传模型失败: {e}")
        raise HTTPException(status_code=500, detail=f"上传模型失败: {str(e)}")

@emotion_router.post("/dataset/ravdess/download")
async def download_ravdess_dataset() -> Dict[str, Any]:
    """
    下载RAVDESS数据集
    返回：下载状态信息
    """
    try:
        result = await model_service.download_ravdess_dataset()
        return result
    except Exception as e:
        logger.error(f"下载RAVDESS数据集失败: {e}")
        raise HTTPException(status_code=500, detail=f"下载数据集失败: {str(e)}")
 